package com.mezz.bones.im.logic.process.impl;


import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.mezz.bones.framework.base.domain.response.Result;
import com.mezz.bones.framework.mybatis.helper.TransactionManagerHelper;
import com.mezz.bones.framework.core.strategy.StrategyContext;
import com.mezz.bones.im.common.constants.MessageConstants;
import com.mezz.bones.im.common.domain.dto.ApplicationDto;
import com.mezz.bones.im.common.enums.*;
import com.mezz.bones.im.common.event.MessageSendWithUserIdEvent;
import com.mezz.bones.im.common.exception.ImBaseException;
import com.mezz.bones.im.common.message.base.MessageHeader;
import com.mezz.bones.im.common.message.body.in.NonReq;
import com.mezz.bones.im.common.message.body.in.PrivateMessageCreateReq;
import com.mezz.bones.im.common.message.body.in.PrivateMessageRecordReq;
import com.mezz.bones.im.common.message.body.out.PrivateMessageCreateRes;
import com.mezz.bones.im.common.message.body.out.PrivateMessageRecordRes;
import com.mezz.bones.im.common.domain.user.ImUserConnectInfo;
import com.mezz.bones.im.common.process.IMessageProcessor;
import com.mezz.bones.im.common.result.ImResultCode;
import com.mezz.bones.im.common.service.IApplicationService;
import com.mezz.bones.im.common.util.MessageUtil;
import com.mezz.bones.im.logic.domain.entity.ChatUser;
import com.mezz.bones.im.logic.notify.domain.request.PrivateMessageAfterSendRequest;
import com.mezz.bones.im.logic.notify.domain.request.PrivateMessageBeforeSendRequest;
import com.mezz.bones.im.logic.notify.domain.response.PrivateMessageBeforeSendResponse;
import com.mezz.bones.im.logic.process.interfaces.AbstractMessageLogicProcess;
import com.mezz.bones.im.logic.service.*;
import com.mezz.bones.im.logic.domain.context.PrivateMessageCreateCtx;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @Author: zhuang
 * @Date: 2024/11/29 18:39
 * @Description: TODO
 * @Version: 1.0
 **/

@Service
public class PrivateMessageCreateProcess extends AbstractMessageLogicProcess<PrivateMessageCreateReq, PrivateMessageCreateRes> {

    /**
     * 上下文参数对象key
     */
    private static final String CREATE_CONTEXT_KEY = "private_message_create_ctx_key";

    @Resource
    private RedissonClient redissonClient;

    @Resource
    private IChatUserService chatUserService;

    @Resource
    private IChatPrivateMessageService chatPrivateMessageService;

    @Resource
    private IChatConversationService chatConversationService;

    @Resource
    private IMessageProcessor messagePublisher;

    @Resource
    private TransactionManagerHelper transactionManagerHelper;

    @Resource
    private StrategyContext strategyContext;

    @Resource
    private IChatAutoReplayService chatAutoReplayService;

    @Resource
    private IMessageReceiveService messageReceiveService;

    @Resource
    private IApplicationService applicationService;

    @Resource
    private IMessageNotifyService messageNotifyService;

    @Resource
    private IUserOnlineStatusService userOnlineStatusService;

    @Resource
    private IChatConversationService conversationService;


    @Override
    public String tag() {
        return ActionCodeEnum.CLIENT_PRIVATE_MESSAGE_CREATE.getCode();
    }

    @Override
    public void preHandlerParam(ImUserConnectInfo senderUserConnectInfo, MessageHeader header, PrivateMessageCreateReq req) {


        if(StrUtil.isBlank(req.getUserId()) && senderUserConnectInfo != null){
            //直接用户发起的消息
            //如果是开放平台接口发起 或者 系统自动发起的消息发送 会主动设置userId
            req.setUserId(senderUserConnectInfo.getUserId());
        }

        if (StrUtil.isBlank(req.getRelationId())) {
            req.setRelationId(MessageConstants.NON_AGENT_MESSAGE_RELATION_ID);
        }

        //默认都是需要同步消息到对端
        if (Objects.isNull(req.getSync2Peer())) {
            req.setSync2Peer(1);
        }

        //默认都是普通消息
        if (Objects.isNull(req.getMsgType())) {
            req.setMsgType(0);
        }
    }

    @Override
    protected void doAfterHandler(ImUserConnectInfo senderUserConnectInfo, MessageHeader header, PrivateMessageCreateReq body, PrivateMessageCreateRes res, Map<String, Object> ctxMap) {

        PrivateMessageCreateCtx ctx = (PrivateMessageCreateCtx) ctxMap.get(CREATE_CONTEXT_KEY);

        //推送单聊消息到对端用户 且是在线状态
        if (body.getSync2Peer() == 1) {
            sendMessage2Peer(ctx);
        }

        applicationNotifyOnAfterCreate(ctx);

//        //自动回复业务规则
//        //可在接入方自定义实现 事件回调方式触发
//        if (autoReplayContent(ctx)) {
//
//            //自动回复消息到用户端
//            autoReplay2User(ctx);
//
//            //发送信令通知到客服端 拉取最近自动回复的消息 补齐聊天记录
//            autoReplay2Customer(ctx);
//
//        }
    }

    private void applicationNotifyOnAfterCreate(PrivateMessageCreateCtx context) {

        //单聊消息 发送后的扩展点业务  自动回复/系统消息发送/其它业务消息推送等

        ApplicationDto applicationDto = applicationService.getByAppId();

        PrivateMessageCreateReq body = context.getBody();
        //现在需求 此事件只处理 文本消息的 敏感词 违禁词信息 所以只处理文本消息内容
        if(!body.getContentType().equals(ContentTypeEnum.TEXT.getType())){
            return;
        }

        ChatUser sender = context.getSender();
        ChatUser receiver = context.getReceiver();

        PrivateMessageAfterSendRequest request = new PrivateMessageAfterSendRequest();
        request.setEventType(ImCallbackEventEnum.PRIVATE_MESSAGE_AFTER_SEND.getType());
        request.setTimestamp(new Date().getTime());
        request.setNonce(RandomUtil.randomString(15));
        request.setUserId(body.getUserId());
        request.setUserType(sender.getUserType());
        request.setPeerId(body.getPeerId());
        request.setPeerType(receiver.getUserType());
        request.setRelationId(body.getRelationId());
        request.setRelationType(MessageUtil.relationType(sender.getUserType(),receiver.getUserType(),body.getRelationId()));
        request.setMsgSeq(body.getMsgSeq());
        request.setMsgType(body.getMsgType());
        request.setSync2Peer(body.getSync2Peer());
        request.setContentType(body.getContentType());
        request.setContent(body.getContent());
        request.setMsgExtra(body.getMsgExtra());

        //签名
        String sign = messageNotifyService.generateNotifySign(applicationDto.getAppSecret(), JSONObject.toJSONString(request));
        String response = messageNotifyService.sendMessageNotify(applicationDto, JSONObject.toJSONString(request),sign);
        if(StrUtil.isBlank(response)){
            return;
        }

        Result afterSendResponse = JSONObject.parseObject(response, new TypeReference<Result>(){});

        if(!afterSendResponse.getCode().equals("S200")){
            throw new ImBaseException(ImResultCode.USER_STATUS_ERROR, afterSendResponse.getMessage());
        }

        //发送后回调 暂不处理
    }

    private void beforePrivateMessageCreate(PrivateMessageCreateCtx context) {
        //可对象消息内容进行替换 合规校验 等操作 或者校验用户状态信息
        //敏感词替换 内容合规校验等 发送双方的状态判断 断言 如果不满足要求 则抛出异常
        //同步回调开发平台接口 应用回调 TODO 重试机制
        applicationNotifyOnBeforeCreate(context);

    }

    private void applicationNotifyOnBeforeCreate(PrivateMessageCreateCtx context) {

        ApplicationDto applicationDto = applicationService.getByAppId();

        MessageHeader header = context.getHeader();
        PrivateMessageCreateReq body = context.getBody();
        //现在需求 此事件只处理 文本消息的 敏感词 违禁词信息 所以只处理文本消息内容
        if(!body.getContentType().equals(ContentTypeEnum.TEXT.getType())){
            return;
        }

        ChatUser sender = context.getSender();
        ChatUser receiver = context.getReceiver();

        PrivateMessageBeforeSendRequest request = new PrivateMessageBeforeSendRequest();
        request.setEventType(ImCallbackEventEnum.PRIVATE_MESSAGE_BEFORE_SEND.getType());
        request.setTimestamp(new Date().getTime());
        request.setNonce(RandomUtil.randomString(15));
        request.setUserId(body.getUserId());
        request.setUserType(sender.getUserType());
        request.setPeerId(body.getPeerId());
        request.setPeerType(receiver.getUserType());
        request.setRelationId(body.getRelationId());
        request.setRelationType(MessageUtil.relationType(sender.getUserType(),receiver.getUserType(),body.getRelationId()));
        request.setMsgSeq(body.getMsgSeq());
        request.setMsgType(body.getMsgType());
        request.setSync2Peer(body.getSync2Peer());
        request.setContentType(body.getContentType());
        request.setContent(body.getContent());
        request.setMsgExtra(body.getMsgExtra());

        //签名
        String sign = messageNotifyService.generateNotifySign(applicationDto.getAppSecret(), JSONObject.toJSONString(request));
        String response = messageNotifyService.sendMessageNotify(applicationDto, JSONObject.toJSONString(request),sign);
        if(StrUtil.isBlank(response)){
            return;
        }

        Result<PrivateMessageBeforeSendResponse> beforeSendResponse = JSONObject.parseObject(response, new TypeReference<Result<PrivateMessageBeforeSendResponse>>(){});

        String callbackCode = beforeSendResponse.getCode();
        if(!callbackCode.equals("S200")){
            //400006 - 用户被禁言
            throw new ImBaseException(callbackCode, beforeSendResponse.getMessage());
        }

        //变更消息内容 其它逻辑先暂时不处理
        PrivateMessageBeforeSendResponse data = beforeSendResponse.getData();
        body.setContent(data.getContent());
        body.setMsgExtra(data.getMsgExtra());
        body.setSync2Peer(data.getSync2Peer());
    }

    private void sendMessage2Peer(PrivateMessageCreateCtx context) {


        //查看用户是否在线
        PrivateMessageCreateReq body = context.getBody();
        MessageHeader header = context.getHeader();

        if (!userOnlineStatusService.isOnline(body.getPeerId())) {
            log.info("用户不在线 不推送消息 | userId:{}", body.getPeerId());
            return;
        }

        //推送实时消息
        List<PrivateMessageRecordRes> res = buildPushPeerMessageModel(context);
        messagePublisher.sendMessage2UserId(new MessageSendWithUserIdEvent(header.getTraceId(), ActionCodeEnum.SERVER_PRIVATE_MESSAGE_RECORD.getCode(), res, body.getPeerId()));

        //推送会话消息（信令消息 通知客户端 主动拉取会话列表）
        NonReq nonReq = new NonReq();
        messagePublisher.sendMessage2UserId(new MessageSendWithUserIdEvent(header.getTraceId(), ActionCodeEnum.SERVER_NOTIFY_PULL_CONVERSATION_RECORD.getCode(), nonReq, body.getPeerId()));

    }

    /**
     * 构建消息体推送到对端用户
     *
     * @param context
     * @return
     */
    private List<PrivateMessageRecordRes> buildPushPeerMessageModel(PrivateMessageCreateCtx context) {

        PrivateMessageCreateReq body = context.getBody();

        PrivateMessageRecordReq req = new PrivateMessageRecordReq();
        req.setMsgKey(body.getMsgKey());
        req.setUserId(body.getPeerId());
        return chatPrivateMessageService.getUserPrivateMessagePage(req);
    }

    private boolean checkMessageRepeat(PrivateMessageCreateCtx context) {

        //TODO 可有专门的判重表数据存储
        return chatPrivateMessageService.existsMsgKey(context.getBody().getMsgKey());
    }

    private void savePrivateMessage(PrivateMessageCreateCtx context) {

        //消息记录落库 双向记录
        chatPrivateMessageService.saveMessagePrivate(context);

        //消息会话更新 双向记录
        chatConversationService.flushChatConversationByPrivateMessage(context);
    }

    private void checkMessageUserInfo(PrivateMessageCreateCtx context) {

        PrivateMessageCreateReq body = context.getBody();
        String userId = body.getUserId();
        String peerId = body.getPeerId();

        ChatUser sender = chatUserService.getByUserId(userId);
        if (sender == null) {
            throw new ImBaseException(ImResultCode.DATA_NOT_FOUND, StrUtil.format("用户ID不存在:{}", userId));
        }

        ChatUser receiver = chatUserService.getByUserId(peerId);
        if (receiver == null) {
            throw new ImBaseException(ImResultCode.DATA_NOT_FOUND, StrUtil.format("用户ID不存在:{}", userId));
        }

        if (StrUtil.isNotBlank(body.getRelationId()) && !body.getRelationId().equals(MessageConstants.NON_AGENT_MESSAGE_RELATION_ID)) {
            ChatUser relationUserInfo = chatUserService.getByUserId(body.getRelationId());
            if (relationUserInfo == null) {
                throw new ImBaseException(ImResultCode.DATA_NOT_FOUND, StrUtil.format("用户ID不存在:{}", userId));
            }

            context.setRelationInfo(relationUserInfo);
        }

        //设置消息双发信息到上下文
        context.setSender(sender);
        context.setReceiver(receiver);


    }

    @Override
    public PrivateMessageCreateRes process(ImUserConnectInfo senderUserConnectInfo, MessageHeader header, PrivateMessageCreateReq req, Map<String, Object> ctxMap) {

        //上下文初始化
        PrivateMessageCreateCtx ctx = new PrivateMessageCreateCtx();
        ctx.setSenderUserConnectInfo(senderUserConnectInfo);
        ctx.setHeader(header);
        ctx.setBody(req);
        ctx.setNow(new Date());

        //校验用户信息
        checkMessageUserInfo(ctx);

        RLock rLock = redissonClient.getLock("private_message:" + header.getTraceId());

        try {
            // 尝试加锁
            boolean lock = false;
            try {
                lock = rLock.tryLock(5000, 15000, TimeUnit.MILLISECONDS);
            } catch (InterruptedException e) {
                throw new ImBaseException(ImResultCode.TRY_LOCK_ERROR);
            }

            if (!lock) {
                throw new ImBaseException(ImResultCode.TRY_LOCK_ERROR);
            }

            //消息重复性判断
            if (checkMessageRepeat(ctx)) {
                throw new ImBaseException(ImResultCode.PARAM_ERROR, StrUtil.format("消息KEY已存在={}", ctx.getHeader().getTraceId()));
            }

            //消息落库前 接入平台扩展业务
            beforePrivateMessageCreate(ctx);

            PrivateMessageCreateCtx finalContext = ctx;
            transactionManagerHelper.executeTransaction(() -> {
                //消息记录落库
               savePrivateMessage(finalContext);
            });

        } finally {
            // 如果未注册事务同步或发生其他情况，确保锁被释放
            if (rLock.isHeldByCurrentThread()) {
                rLock.unlock();
            }
        }

        ctxMap.put(CREATE_CONTEXT_KEY, ctx);

        PrivateMessageCreateRes res = new PrivateMessageCreateRes();
        res.setUserMsgRecordId(ctx.getUserMsgRecordId());
        res.setPeerMsgRecordId(ctx.getPeerMsgRecordId());
        res.setMsgSeq(ctx.getBody().getMsgSeq());
        res.setNow(ctx.getNow());
        return res;
    }
}
